/*
 * $Id: channel.cpp 9ce65bed8ef4 2012/08/26 02:14:51 pgurumur $
 * Copyright (c) 2012 Prabhu Gurumurthy <pgurumur@gmail.com>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *

 * __version__  = "$Revision: 9ce65bed8ef4 $"
 * __author__   = "$Author: pgurumur $"
 * __modified__ = "$Date: 2012-08-26 02:14:51Z $"
 */
#include <assert.h>
#include <err.h>
#include <stdio.h>

#include <iostream>
#include <string>

#include "consts.h"
#include "channel.h"

channel::channel(int sock) : _channel(0), _fds(0), _sock(sock)
{
   if ((_fds = (struct pfd *)malloc(sizeof(struct pfd))) == NULL) {
      err(1, "malloc(): ");
   }
   else {
      _fds[0].fd = _sock;
      _fds[0].events = POLLIN | POLLOUT | POLLRDNORM;
   }
}

channel::channel(const channel &Channel)
{
   *this = Channel;
}

channel &
channel::operator= (const channel &Channel)
{

   _channel = Channel._channel;
   return *this;
}

channel::~channel()
{
   if (_channel) {
      if (libssh2_channel_send_eof(_channel) < 0)
         warn("libssh2_channel_send_eof");

      if (libssh2_channel_close(_channel) < 0)
         warn("libssh2_channel_close");

      if (libssh2_channel_wait_closed(_channel) < 0)
         warn("libssh2_channel_wait_closed");

      libssh2_channel_free(_channel);
      free(_fds);
   }
}


void
channel::open(LIBSSH2_SESSION *session)
{
   if (session) {
      _channel = libssh2_channel_open_session(session);
      assert(_channel);

      if (libssh2_channel_request_pty(_channel, DefTerm.c_str()) == 0)
         if (libssh2_channel_shell(_channel))
            err(1, "libssh2_channel_shell()");
   }
}

bool
channel::eof()
{
   return (bool)libssh2_channel_eof(_channel);
}


int
channel::cread(int32_t bufsize)
{
   if (bufsize == 0)
      bufsize = 1024;

   bool flag = true;
   int wrbytes = 0;
   char buffer[MaxLine];
   do {
      int bytes = libssh2_channel_read(_channel, buffer, MaxLine);
      if (bytes == LIBSSH2_ERROR_EAGAIN) {
         std::cout << bytes;
         continue;
      }
      else if (bytes < 0) {
         flag = false;
      }
      else {
         std::cout << buffer << std::endl;
         wrbytes = write(fileno(stderr), buffer, bytes);
      }
   } while (flag);

   return wrbytes;
}

void
channel::cwrite(const char *buffer)
{
   int tosize = strlen(buffer);
   int written = 0;

   bool wait = true;
   do {
      if (_fds[0].revents & LIBSSH2_POLLFD_POLLOUT) {
         int bytes = libssh2_channel_write(_channel, buffer, sizeof(buffer));
         if (bytes == LIBSSH2_ERROR_EAGAIN) {
            std::cout << bytes;
            continue;
         }
         else if (bytes < 0) {
            err(1, "write()");
         }
         else if (bytes < tosize) {
            written += bytes;
            continue;
         }
         else if (bytes == tosize) {
            wait = false;
         }
      }
   } while (wait);
}

void
channel::shell()
{
   bool wait = true;
   std::cout << "here.... " << std::endl;
   do {
      int rc = libssh2_poll(_fds, 1, 10);
      int act = 0;

      if (rc < 1)
         continue;

      if (_fds[0].revents & LIBSSH2_POLLFD_POLLIN) {
         char buffer[BUFSIZ];
         memset(buffer, '\0', BUFSIZ);
         int ix = libssh2_channel_read(channel, buffer, sizeof(buffer));
         act++;

         if (ix == LIBSSH2_ERROR_EAGAIN) {
            rereads++;
         }
         else if (ix < 0)
            err(1, "read failed");
         else {
            totread += ix;
            std::cout << "read " << ix << "bytes (" << totread << "in total\n";
         }
      }
      else if (_fds[0].revents & LIBSSH2_POLLFD_CHANNEL_CLOSED) {
         wait = false;
      }
      else if (_fds[0].revents & LIBSSH2_POLLFD_POLLOUT) {
         std::cout << "No I am here..." << std::endl;
         char buf[10];
         memset(buf, '\0', 10);
         int rb = read(fileno(stdin),  buf, sizeof(buf));
         if (rb < 0)
            err(1, "read()");
         else
            cwrite(buf);
      }

   } while (wait);

}

